package com.example.esp8266;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MqttConnectUtil {

    private MqttClient client;
    private MqttConnectOptions options;
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final String TAG = getClass().getSimpleName();

    private String productKey;
    private String deviceName;
    private String deviceSecret;
    private String pub_topic;
    private String sub_topic;
    private MqttUtilConnectListener listener;

    private final Handler handler = new Handler(Looper.getMainLooper());

    private MqttConnectUtil(String productKey, String deviceName, String deviceSecret, String pub_topic, String sub_topic) {
        this.productKey = productKey;
        this.deviceName = deviceName;
        this.deviceSecret = deviceSecret;
        this.pub_topic = pub_topic;
        this.sub_topic = sub_topic;
        initMqtt();
    }

    public MqttConnectUtil(MqttParamsWrapper wrapper) {
        this.productKey = wrapper.getProductKey();
        this.deviceName = wrapper.getDeviceName();
        this.deviceSecret = wrapper.getDeviceSecret();
        this.pub_topic = wrapper.getPub_topic();
        this.sub_topic = wrapper.getSub_topic();
        initMqtt();
    }

    public void resetParams(MqttParamsWrapper wrapper){
        this.productKey = wrapper.getProductKey();
        this.deviceName = wrapper.getDeviceName();
        this.deviceSecret = wrapper.getDeviceSecret();
        this.pub_topic = wrapper.getPub_topic();
        this.sub_topic = wrapper.getSub_topic();
        if (client.isConnected()) disconnect();
        initMqtt();
    }

    private void initMqtt() {
        try {
            String clientId = "a18OIhtmIk5.test_device1";
            Map<String, String> params = new HashMap<>(16);
            params.put("productKey", productKey);
            params.put("deviceName", deviceName);
            params.put("clientId", clientId);
            String timestamp = String.valueOf(System.currentTimeMillis());
            params.put("timestamp", timestamp);
            // cn-shanghai
            String host_url = "tcp://" + productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com:1883";
            String client_id = clientId + "|securemode=2,signmethod=hmacsha1,timestamp=" + timestamp + "|";
            String user_name = deviceName + "&" + productKey;
            String password = AliyunIoTSignUtil.sign(params, deviceSecret, "hmacsha1");

            //host为主机名，test为clientid即连接MQTT的客户端ID，一般以客户端唯一标识符表示，MemoryPersistence设置clientid的保存形式，默认为以内存保存
            Log.w(TAG, ">>>" + host_url);
            Log.w(TAG, ">>>" + client_id);

            //connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);

            client = new MqttClient(host_url, client_id, new MemoryPersistence());
            //MQTT的连接设置
            options = new MqttConnectOptions();
            //设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录，这里设置为true表示每次连接到服务器都以新的身份连接
            options.setCleanSession(false);
            //设置连接的用户名
            options.setUserName(user_name);
            //设置连接的密码
            options.setPassword(password.toCharArray());
            // 设置超时时间 单位为秒
            options.setConnectionTimeout(10);
            // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线，但这个方法并没有重连的机制
            options.setKeepAliveInterval(60);
            //设置回调
            //client.setCallback(callback);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void connect() {
        scheduler.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (!client.isConnected()) {
                    mqttConnect();
                }
            }
        }, 0, 10 * 1000, TimeUnit.MILLISECONDS);
    }

    private void mqttConnect() {
        try {
            if (!(client.isConnected())) {
                client.connect(options);
                if (listener != null) {
                    handler.post(() -> listener.onConnectSuccess(client,MqttConnectUtil.this));
                }
            }
        } catch (Exception e) {
            if (listener != null) {
                handler.post(() -> listener.onConnectFailure(e));
            }
        }
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//
//            }
//        }).start();
    }

    public void publishMessage(String message) {
        if (client == null || !client.isConnected()) {
            return;
        }
        MqttMessage mqtt_message = new MqttMessage();
        mqtt_message.setPayload(message.getBytes());
        try {
            client.publish(pub_topic, mqtt_message);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    public void subscribeTopic(){
        try {
            client.subscribe(sub_topic,1);
        } catch (MqttException e) {
            throw new RuntimeException(e);
        }
    }


    public MqttConnectUtil setCallback(MqttCallback callback) {
        if (client == null) throw new RuntimeException("工具类尚未初始化");
        client.setCallback(callback);
        return this;
    }

    public MqttConnectUtil setListener(MqttUtilConnectListener listener) {
        this.listener = listener;
        return this;
    }

    public boolean isConnected() {
        return client.isConnected();
    }

    public void disconnect() {
        try {
            listener = null;
            client.setCallback(null);
            client.disconnect();
            client.close();
        } catch (MqttException e) {
            throw new RuntimeException(e);
        }
    }
}
